package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.redis.util.RedisLockUtil;
import com.spzx.product.domain.ProductSku;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.domain.dto.SkuPriceDTO;
import com.spzx.product.domain.dto.SkuStockDTO;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.ProductSkuService;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class ProductSkuServiceImpl extends ServiceImpl<ProductSkuMapper , ProductSku>
        implements ProductSkuService {
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    RedisTemplate redisTemplate;
    @Resource
    SkuStockMapper skuStockMapper;
    //验价业务
    @Override
    public List<SkuPriceDTO> checkSkuPrice(List<SkuPriceDTO> skuPriceDTOS) {
        if(CollectionUtils.isEmpty(skuPriceDTOS)){
            throw new ServiceException("订单项列表为空");
        }

        skuPriceDTOS.forEach(skuPriceDTO -> {
            //查询数据库该商品的最新价格判断
            ProductSku productSku = baseMapper.selectById(skuPriceDTO.getSkuId());
            //比较价格
            skuPriceDTO.setFlag(skuPriceDTO.getSkuPrice().compareTo(productSku.getSalePrice())==0);
        });

        return skuPriceDTOS;
    }
    //业务完整性： 锁库存需要一起成功或失败
    @Override
    public List<SkuStockDTO> checkAndLockSkuStock(List<SkuStockDTO> skuStockDTOS) {
        if(CollectionUtils.isEmpty(skuStockDTOS)){
            throw new ServiceException("订单项列表为空");
        }
        //1、遍历 一个个sku进行库存校验锁定
        skuStockDTOS.forEach(skuStockDTO -> {
           checkLockStock(skuStockDTO);
        });
        //2、如果有锁库存失败的，还有锁库存成功的： 锁定成功的应该释放库存
        //2.1 判断是否有锁定库存失败的
        //获取锁定库存成功的集合
        List<SkuStockDTO> lockedSuccessDTOS = skuStockDTOS.stream().filter(skuStockDTO -> skuStockDTO.isFlag())
                .collect(Collectors.toList());
        if(CollectionUtils.isEmpty(lockedSuccessDTOS) || //全部失败
                lockedSuccessDTOS.size()!=skuStockDTOS.size()){//部分失败
            //2.2 如果有，需要获取锁定库存成功的skuStockDTO对象 释放他锁定的库存
            lockedSuccessDTOS.forEach(skuStockDTO -> {
               skuStockMapper.releaseStock(skuStockDTO);
            });
        }else{
            //验库存锁库存成功： 缓存锁定成功的库存数据 以便延迟关单
            redisTemplate.opsForValue()
                    .set("spzx:order:stock:cache:"+skuStockDTOS.get(0).getOrderNo() , skuStockDTOS);
        }
        //3、返回验库存的结果
        return skuStockDTOS;
    }

    //验证一个sku的库存 锁定库存的业务
    private void checkLockStock(SkuStockDTO skuStockDTO){
        //验库存判断 和修改库存的锁定 需要保证原子性执行：
        //1、获取分布式锁：  使用skuId加锁：
        String lockKey = "spzx:product:stock:lock:"+ skuStockDTO.getSkuId();
        String uuid = UUID.randomUUID().toString().replace("-","");
        try {
            RedisLockUtil.lock(stringRedisTemplate,lockKey,uuid,10 );
            //2、执行验库存锁库存的业务
//            //2.1 查询skuId商品的库存: sku_stock表通过sku_id关联
//            SkuStock skuStock = SskuStockMapper.selectOne(new LambdaQueryWrapper<>(SkuStock.class)
//                    .eq(SkuStock::getSkuId ,skuStockDTO.getSkuId())
//                    .ge(SkuStock::getAvailableNum , skuStockDTO.getSkuNum())
//                    .last("limit 1"));
//            //2.2 修改查询到的skuStock的锁定库存数量和可用库存数量

            //2.1+2.2: 使用更新语句的条件判断 进行更新，成功表示库存足够锁定库存成功
            //  update sku_stock set lock_num = lock_num +skuNum , available_num = available_num - skuNum
            //  where sku_id = skuId and available_num >= skuNum and del_flag = 0;
            boolean flag = skuStockMapper.checkAndLockStock(skuStockDTO);//>0表示成功
            skuStockDTO.setFlag(flag);
        } finally {
            //3、释放分布式锁
            RedisLockUtil.unlock(stringRedisTemplate,lockKey,uuid);
        }
    }
}
